{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CFS字符分割-1Z实验室"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概要"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**CFS** 的全称叫做**Color Filling Segmentation**颜色填充字符分割。\n",
    "\n",
    "它基于一个淳朴的假设，一个字符就是一个连通域，都是连在一起的。 \n",
    "\n",
    "首先检索到图片中一个非零点，检索顺序， 从上到下， 从左到右。 然后将这个点添加到队列中。借助队列，检索上下左右相邻区域，直至蔓延到整个连通域。当前的连通域就认作是一个字符。\n",
    "完成后，再继续寻找下一个非零的点。\n",
    "\n",
    "![cfs_result.png](cfs_result.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 读入图片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from matplotlib import pyplot  as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "img = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)\n",
    "plt.imshow(img, cmap='gray')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 寻找非零点坐标\n",
    "\n",
    "利用`visited`矩阵，记录该点是否被遍历过。 从上到下， 从左到右检索到第一个不为0的点，然后返回该点坐标。\n",
    "如果未检索到，则返回`(-1,-1)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def findNextStartPoint(img, visited):\n",
    "    '''\n",
    "    检索一个不为0的点\n",
    "    '''\n",
    "    PT_VISITED = 255\n",
    "    # 原来的二值图片 去掉遍历过的点\n",
    "    targetArea = cv2.bitwise_and(img, cv2.bitwise_not(visited))\n",
    "    height,width = img.shape\n",
    "    \n",
    "    for y in range(height):\n",
    "        for x in range(width):\n",
    "            # 从上到下， 从左到右检索第一个不为0的点的坐标\n",
    "            if visited[y][x]!= PT_VISITED and img[y][x] != 0:\n",
    "                return (x,y)\n",
    "    #不存在白点\n",
    "    return (-1, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 检索连通域\n",
    "\n",
    "`findNextStartPoint`找到起始点， 然后，将这个坐标传入`findConnectedDomain`中， 作为起始检索点。\n",
    "利用数据结构**队列 Queue**来记录像素点的扩散遍历。 扩张方向为十字形， 上下左右， 如果像素点为非零，而且没有被遍历过，就添加到队列中。\n",
    "如果队列为空，则标志着当前连通域遍历完成。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def findConnectedDomain(img, start_pt):\n",
    "    '''\n",
    "        从起始点start_pt  (x, y) 找到该点所在的连通域\n",
    "    '''\n",
    "    PT_VISITED = 255\n",
    "    \n",
    "    height, width = img.shape\n",
    "    # 十字型核 \n",
    "    cross_kernel = ((0,1), (1,0), (-1,0), (0,-1))\n",
    "    # 利用与img同等大小的矩阵记录访问点\n",
    "    visited = np.zeros_like(img)\n",
    "    # 初始化检索队列\n",
    "    queue = [start_pt]\n",
    "    \n",
    "    while len(queue) != 0:\n",
    "        # 如果队列不为空就一直执行\n",
    "        # 弹出队首元素\n",
    "        cur_pt = queue.pop()\n",
    "        (x,y) = cur_pt\n",
    "        \n",
    "        if visited[y][x] == PT_VISITED:\n",
    "            # 如果已经访问过就PASS\n",
    "            continue\n",
    "        # 标记访问过当前元素\n",
    "        visited[y][x] = PT_VISITED\n",
    "        \n",
    "        for delta_x,delta_y in cross_kernel:\n",
    "            # 下一个点的x坐标与y坐标\n",
    "            next_x = x + delta_x \n",
    "            next_y = y + delta_y\n",
    "            if next_x < 0 or next_x >= width or next_y < 0 or next_y >= height:\n",
    "                # 索引越界判断\n",
    "                continue\n",
    "            if visited[next_y][next_x] == PT_VISITED:\n",
    "                # 已经访问过\n",
    "                continue\n",
    "            if img[next_y][next_x] > 0:\n",
    "                # 将符合条件的邻居添加到队列里\n",
    "                queue.append((next_x, next_y))\n",
    "\n",
    "    return visited\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CFS函数\n",
    "\n",
    "不断寻找一个非0点像素坐标，然后寻找该点所在的整个连通域。\n",
    "函数返回各个连通域对应的**罩层 Mask**数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def CFS(img):\n",
    "    '''\n",
    "    利用cfs算法，寻找各个连通域，并返回连通域的掩模数组\n",
    "    '''\n",
    "    digit_masks = []\n",
    "    visited = np.zeros_like(img)\n",
    "    \n",
    "    while True:\n",
    "        # 寻找一个非0的起始点\n",
    "        start_pt = findNextStartPoint(img, visited)\n",
    "        print('Start From {}'.format(start_pt))\n",
    "        if start_pt[0] == -1 and start_pt[1] == -1:\n",
    "            # 如果无有效起始点， 就结束\n",
    "            break\n",
    "        # 获取该点所在连通域的掩模\n",
    "        digit_mask = findConnectedDomain(img, start_pt)\n",
    "        digit_masks.append(digit_mask)\n",
    "        # 更新访问矩阵\n",
    "        cv2.bitwise_or(visited, digit_mask, visited)\n",
    "    \n",
    "    return digit_masks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Start From (307, 164)\n",
      "Start From (176, 172)\n",
      "Start From (396, 173)\n",
      "Start From (146, 181)\n",
      "Start From (142, 203)\n",
      "Start From (21, 216)\n",
      "Start From (404, 249)\n",
      "Start From (412, 253)\n",
      "Start From (-1, -1)\n"
     ]
    }
   ],
   "source": [
    "digit_masks = CFS(img)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 效果展示\n",
    "\n",
    "创建画布，用不同的颜色填充不同的连通域。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "color: [ 57 240 129]\n",
      "color: [ 19  20 249]\n",
      "color: [103 123 170]\n",
      "color: [152 183  59]\n",
      "color: [188 157  25]\n",
      "color: [141  36 237]\n",
      "color: [  8 132  80]\n",
      "color: [224  34 207]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''\n",
    "在一张图片里面 使用不同的色彩对连通域进行绘制\n",
    "'''\n",
    "height,width = img.shape\n",
    "canvas = np.zeros((height, width, 3))\n",
    "\n",
    "for didx, mask in enumerate(digit_masks):\n",
    "    digit = np.zeros((height, width, 3))\n",
    "    color = np.random.randint(0, 255, (3))\n",
    "    print('color: {}'.format(color))\n",
    "    digit[:] = color\n",
    "    digit = cv2.bitwise_or(canvas, digit, mask=mask)\n",
    "    canvas += digit\n",
    "\n",
    "## 保存图片\n",
    "cv2.imwrite('cfs_result.png', canvas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "img = cv2.imread('cfs_result.png')\n",
    "plt.imshow(img[:,:,::-1])\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
